package com.uinnova.product.eam.service.dm.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.binary.core.exception.BinaryException;
import com.binary.core.lang.NumberUtils;
import com.binary.core.util.BinaryUtils;
import com.binary.jdbc.Page;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.uinnova.product.eam.base.exception.ServerException;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.model.es.EamDiagramRelationSys;
import com.uinnova.product.eam.config.Env;
import com.uinnova.product.eam.model.CiInfoExtend;
import com.uinnova.product.eam.model.EamArtifactVo;
import com.uinnova.product.eam.model.bm.SavePrivateBatchCIContext;
import com.uinnova.product.eam.model.diagram.DiagramNodeJson;
import com.uinnova.product.eam.model.dm.*;
import com.uinnova.product.eam.model.dm.bean.AttrAndCiDto;
import com.uinnova.product.eam.model.dm.bean.DataModelEntityNodeVo;
import com.uinnova.product.eam.model.dto.DealAttributeDto;
import com.uinnova.product.eam.model.dto.MoveAttributeDto;
import com.uinnova.product.eam.model.enums.ArtifactEnum;
import com.uinnova.product.eam.model.vo.EntityAndAttributeVo;
import com.uinnova.product.eam.service.EamDiagramRelationSysService;
import com.uinnova.product.eam.service.ICISwitchSvc;
import com.uinnova.product.eam.service.IEamArtifactSvc;
import com.uinnova.product.eam.service.IEamCIClassApiSvc;
import com.uinnova.product.eam.service.dm.DataModelSvc;
import com.uinnova.product.eam.service.es.IamsESCmdbCommPrivateSvc;
import com.uinnova.product.eam.service.impl.IamsCIPrivateSvc;
import com.uinnova.product.eam.service.impl.IamsCIRltSwitchSvc;
import com.uinnova.product.vmdb.comm.model.ci.CCcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.comm.model.ci.CcCiAttrDef;
import com.uinnova.product.vmdb.comm.util.CommUtil;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiClassInfo;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.product.vmdb.provider.rlt.bean.CcCiRltInfo;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.model.*;
import com.uinnova.project.db.eam.ESDiagramNodeDao;
import com.uino.api.client.cmdb.IRltClassApiSvc;
import com.uino.bean.cmdb.base.*;
import com.uino.bean.cmdb.business.BindCiRltRequestDto;
import com.uino.bean.cmdb.query.ESAttrBean;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.cmdb.query.ESRltSearchBean;
import com.uino.dao.cmdb.CiClassProDropSourceDefHelper;
import com.uino.dao.cmdb.ESCIClassSvc;
import com.uino.dao.util.ESUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author wcl
 * @description 数据建模相关业务
 */
@Slf4j
@Service
public class DataModelSvcImpl implements DataModelSvc {

    @Resource
    private ICISwitchSvc iciSwitchSvc;
    @Resource
    private DataModelQuerySvcImpl modelQuerySvc;
    @Resource
    private ESCIClassSvc classSvc;
    @Resource
    private EamDiagramRelationSysService sysService;
    @Resource
    private IEamCIClassApiSvc ciClassApiSvc;
    @Resource
    private IamsCIPrivateSvc ciPrivateSvc;
    @Resource
    private IRltClassApiSvc rltClassApiSvc;
    @Resource
    private IamsCIRltSwitchSvc rltSwitchSvc;
    @Resource
    private ESDiagramApiClient diagramApiClient;
    @Resource
    private ESDiagramNodeDao nodeDao;

    @Autowired
    @Lazy
    IamsESCmdbCommPrivateSvc commSvc;
    @Resource
    private IEamArtifactSvc artifactSvc;

    private static final String LOGIC = "企业级";
    private static final String SYSTEM = "系统级";

    @Override
    public DataModelCiInfo saveOrUpdate(DealAttributeDto dto) {
        CcCiInfo ciInfo = dto.getCiInfo();
        Long rltClassId = getDmRltClassId();
        String ownerCode = dto.getCiInfo().getCi().getOwnerCode();
        if(ownerCode==null){
            ownerCode = SysUtil.getCurrentUserInfo().getLoginCode();
            ciInfo.getCi().setOwnerCode(ownerCode);
        }
        Long classId = dto.getCiInfo().getCi().getClassId();
        ESCIClassInfo classInfo =null;
        if(!BinaryUtils.isEmpty(classId)){
            classInfo = ciClassApiSvc.queryClassById(classId);
        }
        String classCode = null;
        if(!BinaryUtils.isEmpty(dto.getCiInfo().getCiClass())){
            classCode = dto.getCiInfo().getCiClass().getClassCode();
        }
        if(BinaryUtils.isEmpty(classInfo) && !BinaryUtils.isEmpty(classCode)){
            CcCiClassInfo ciClass = ciClassApiSvc.getCIClassByCodes(ciInfo.getCiClass().getClassCode());
            classInfo = EamUtil.copy(ciClass.getCiClass(), ESCIClassInfo.class);
        }
        if(BinaryUtils.isEmpty(classInfo)){
            throw new ServerException("对象分类不存在或已删除,请更新制品信息!");
        }
        classCode = classInfo.getClassCode();
        //新建或从标准库拖
        DataModelCiInfo result;
        if(Env.ATTRIBUTES.getCode().equals(classCode) || Env.STANDARD.getCode().equals(classCode)){
            BinaryUtils.checkEmpty(dto.getCiCode(), "实体Code");
            List<ESCIInfo> entityList = iciSwitchSvc.getCiByCodes(Lists.newArrayList(dto.getCiCode()), ownerCode, LibType.PRIVATE);
            if(BinaryUtils.isEmpty(entityList)){
                throw new ServerException("实体数据已被删除，请检查!");
            }
            DataModelDiagramParam modelParam = getDiagramParam(ciInfo.getCi().getDiagramId(), ciInfo.getCi().getSheetId());
            result = saveAttribute(modelParam, entityList.get(0), dto.getCopyFlag(), rltClassId, classCode, ciInfo);
        }else if(Env.LOGIC_ENTITY.getCode().equals(classCode) || Env.CONCEPTION_ENTITY.getCode().equals(classCode)
                    || Env.PHYSICAL_ENTITY.getCode().equals(classCode)){
            result = saveEntity(dto, rltClassId, classInfo, ciInfo, ownerCode);
        } else {
            Long id = iciSwitchSvc.saveOrUpdateCI(ciInfo, LibType.PRIVATE);
            CcCiInfo ciNew = iciSwitchSvc.getCiInfoById(id, LibType.PRIVATE);
            result = EamUtil.copy(ciNew, DataModelCiInfo.class);
            result.setAttributeDefs(classInfo.getCcAttrDefs());
            result.setAttributeCiClass(classInfo);
        }
        result.getCi().setShape(classInfo.getShape());
        return result;
    }

    @Override
    public Integer saveOrUpdateAttrBatch(List<ESCIInfo> ciList) {
        Set<Long> classIds = new HashSet<>();
        Set<String> entityCodes = new HashSet<>();
        for (ESCIInfo each : ciList) {
            if(BinaryUtils.isEmpty(each.getCiCode())){
                throw new BinaryException("存在ciCode为空的元素");
            }
            classIds.add(each.getClassId());
            Object entityObj = each.getAttrs().get(DataModelAttribute.ENTITY);
            if(entityObj == null){
                continue;
            }
            entityCodes.add(entityObj.toString());
        }
        Long rltClassId = getDmRltClassId();
        String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        String ownerCode = ciList.get(0).getOwnerCode();
        if (BinaryUtils.isEmpty(ownerCode)){
            ownerCode = loginCode;
        }
        Map<String, SavePrivateBatchCIContext> contextMap = ciPrivateSvc.saveOrUpdateBatchCI(ciList, new ArrayList<>(classIds), ownerCode, loginCode);
        List<ESCIInfo> entityList = iciSwitchSvc.getCiByCodes(new ArrayList<>(entityCodes), ownerCode, LibType.PRIVATE);
        Map<String, ESCIInfo> entityMap = entityList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        //批量绑定关系
        List<BindCiRltRequestDto> bindList = new ArrayList<>();
        for (Map.Entry<String, SavePrivateBatchCIContext> each : contextMap.entrySet()) {
            String entityCode = each.getValue().getAttrsStr().get(DataModelAttribute.ENTITY);
            if(entityCode == null){
                continue;
            }
            ESCIInfo entity = entityMap.get(entityCode);
            if(entity == null){
                continue;
            }
            BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(ownerCode).repetitionError(false).rltClassId(rltClassId)
                    .custom1("3").sourceCiId(entity.getId()).targetCiId(each.getValue().getEsCi().getId()).build();
            bindList.add(bindRlt);
        }
        rltSwitchSvc.bindCiRltBatch(bindList, LibType.PRIVATE);
        return 1;
    }

    private synchronized DataModelCiInfo saveEntity(DealAttributeDto dto, Long rltClassId, ESCIClassInfo classInfo, CcCiInfo ciInfo, String ownerCode){
        //判断是否重名
        String name = ciInfo.getAttrs().get(DataModelEntity.NAME_CN);
        String type = ciInfo.getAttrs().get(DataModelEntity.ENTITY_TYPE);
        if(Env.LOGIC_ENTITY.getCode().equals(classInfo.getClassCode()) && BinaryUtils.isEmpty(type)){
            DataModelDiagramParam modelParam = getDiagramParam(ciInfo.getCi().getDiagramId(), ciInfo.getCi().getSheetId());
            String viewType = modelParam.getDiagram().getViewType();
            if(!BinaryUtils.isEmpty(viewType)){
                EamArtifactVo artifact = artifactSvc.queryArtifact(Long.parseLong(viewType));
                type = ArtifactEnum.SYS_LOGICAL_ENTITY.getArtifactType().equals(artifact.getTypeClassification()) ? SYSTEM : LOGIC;
            }else{
                type = LOGIC;
            }
            ciInfo.getAttrs().put(DataModelEntity.ENTITY_TYPE, type);
        }
        String oldCiCode = ciInfo.getCi().getCiCode();
        String checkCode = type == null?"" : "-" + type;
        Map<String, ESCIInfo> nameMap = queryNameMap(classInfo.getId(), checkCode, ownerCode);
        Long removeId = setEntityName(nameMap, ciInfo, checkCode, dto.getLibType());
        if(removeId != null){
            iciSwitchSvc.removeById(removeId, null, LibType.PRIVATE);
        }
        ciInfo.getCi().setOwnerCode(ownerCode);
        if(type != null){
            ciInfo.getAttrs().put(DataModelEntity.ENTITY_TYPE, type);
        }
        Long id = iciSwitchSvc.saveOrUpdateCI(ciInfo, LibType.PRIVATE);
        CcCiInfo result = iciSwitchSvc.getCiInfoById(id, LibType.PRIVATE);
        if(LibType.DESIGN.equals(dto.getLibType())){
            //设计库-私有库ciCode映射
            Map<String, String> ciCodeMap = new HashMap<>();
            ciCodeMap.put(oldCiCode, result.getCi().getCiCode());
            Map<String, Long> entityMap = new HashMap<>();
            entityMap.put(result.getCi().getCiCode(), result.getCi().getId());
            copyDesignAttr(ciCodeMap, entityMap, ownerCode);
        }
        return EamUtil.copy(result, DataModelCiInfo.class);
    }

    private synchronized DataModelCiInfo saveAttribute(DataModelDiagramParam modelParam, ESCIInfo entity, Boolean copyFlag, Long rltClassId, String classCode, CcCiInfo ciInfo){
        ESDiagramNode entityNode = modelParam.getNodeMap().get(entity.getCiCode());
        DataModelAttributeParam attributeParam = getJsonName(entityNode);
        String name = ciInfo.getAttrs().get(DataModelAttribute.NAME_CN);
        if(BinaryUtils.isEmpty(name) || copyFlag){
            name = BinaryUtils.isEmpty(name)?"属性":name;
            //兼容拖入实体属性形状情况
            DataModelCodeNameVo attrNameParam = checkName(attributeParam, name, null, EamUtil.copy(ciInfo, DataModelCiInfo.class));
            ciInfo.getAttrs().put(DataModelAttribute.NAME_CN, attrNameParam.getName());
        }else{
            DataModelEntityNodeVo attrNode = attributeParam.getNameMap().get(name);
            String ciCode = ciInfo.getCi().getCiCode();
            if(!BinaryUtils.isEmpty(attrNode) && (BinaryUtils.isEmpty(ciCode) || !attrNode.getCiCode().equals(ciCode))){
                throw new ServerException("当前实体已存在【"+name+"】属性项!");
            }
        }
        String entityCiCode = ciInfo.getAttrs().get(DataModelAttribute.ENTITY);
        if((Env.ATTRIBUTES.getCode().equals(classCode) && !entity.getCiCode().equals(entityCiCode)) || copyFlag){
            ciInfo.getCi().setCiCode(null);
        }
        ciInfo.getAttrs().put(DataModelAttribute.ENTITY, entity.getCiCode());
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        if(Env.STANDARD.getCode().equals(classCode)){
            coverStandardToAttribute(attrClass.getCiClass().getId(), ciInfo);
        }
        if(BinaryUtils.isEmpty(ciInfo.getCi().getId())){
            ciInfo.getAttrs().put(DataModelAttribute.FOREIGN_KEY, Boolean.toString(false));
            ciInfo.getAttrs().put(DataModelAttribute.INHERIT_ID, Long.toString(ESUtil.getUUID()));
        }
        Long id = iciSwitchSvc.saveOrUpdateCI(ciInfo, LibType.PRIVATE);
        CcCiInfo resultCi = iciSwitchSvc.getCiInfoById(id, LibType.PRIVATE);
        DataModelCiInfo result = EamUtil.copy(resultCi, DataModelCiInfo.class);
        result.setAttributeDefs(attrClass.getAttrDefs());
        result.setAttributeCiClass(attrClass.getCiClass());
        if(Env.STANDARD.getCode().equals(classCode)){
            CcCiClassInfo standardCiClass = ciClassApiSvc.getCIClassByCodes(Env.STANDARD.getCode());
            result.getCiClass().setShape(standardCiClass.getCiClass().getShape());
        }
        BindCiRltRequestDto reqBean = BindCiRltRequestDto.builder().sourceCiId(entity.getId())
                .targetCiId(result.getCi().getId()).ownerCode(modelParam.getOwnerCode()).rltClassId(rltClassId).build();
        rltSwitchSvc.bindCiRlt(reqBean, LibType.PRIVATE);
        // 从当前实体开始查看视图上实体间关系
        String primaryKey = result.getAttrs().get(DataModelAttribute.PRIMARY_KEY);
        if(!BinaryUtils.isEmpty(primaryKey) && Boolean.parseBoolean(primaryKey)){
            Map<String, List<CcCiInfo>> inheritMap = inheritAttribute(entity.getCiCode(), modelParam, Lists.newArrayList(result));
            updateInheritDiagramNode(modelParam.getModel(), inheritMap);
            result.setInheritMap(inheritMap);
        }
        return result;
    }


    @Override
    public DataModelCiInfo copyAttribute(DealAttributeDto dto) {
        List<CcCiInfo> ciList = dto.getCiList();
        CcCi attrCi = ciList.get(0).getCi();
        String ownerCode = attrCi.getOwnerCode();
        List<ESCIInfo> entityList = iciSwitchSvc.getCiByCodes(Lists.newArrayList(dto.getCiCode()), ownerCode, LibType.PRIVATE);
        if(BinaryUtils.isEmpty(entityList)){
            throw new ServerException("实体数据已被删除，请检查!");
        }
        Long rltClassId = getDmRltClassId();
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        Long entityId = entityList.get(0).getId();
        String entityCode = entityList.get(0).getCiCode();
        List<CcCiRltInfo> rltList = getAttrByEntity(rltClassId, attrClass.getCiClass().getId(), Sets.newHashSet(entityCode), ownerCode, LibType.PRIVATE);
        Set<String> cnNameSet = new HashSet<>();
        for (CcCiRltInfo each : rltList) {
            CcCiInfo attr = each.getTargetCiInfo();
            String cnName = attr.getAttrs().get(DataModelAttribute.NAME_CN);
            if(!BinaryUtils.isEmpty(cnName)){
                cnNameSet.add(cnName);
            }
        }
        for (CcCiInfo each : ciList) {
            each.getCi().setCiCode(Long.toString(ESUtil.getUUID()));
            each.getCi().setOwnerCode(ownerCode);
            String name = each.getAttrs().get(DataModelAttribute.NAME_CN);
            if(BinaryUtils.isEmpty(name)){
                name = "属性";
            }
            String prefix = name;
            int num = 1;
            if(name.contains("-")){
                String suffix = name.substring(name.lastIndexOf("-")+1);
                if(!BinaryUtils.isEmpty(suffix) && NumberUtils.isInteger(suffix)){
                    prefix = name.substring(0, name.lastIndexOf("-"));
                }
            }
            while (cnNameSet.contains(prefix + "-" + num)){
                num++;
            }
            cnNameSet.add(prefix + "-" + num);
            each.getAttrs().put(DataModelAttribute.NAME_CN, prefix + "-" + num);
            each.getAttrs().put(DataModelAttribute.ENTITY, entityCode);
            each.getAttrs().put(DataModelAttribute.FOREIGN_KEY, Boolean.toString(false));
            each.getAttrs().put(DataModelAttribute.INHERIT_ID, Long.toString(ESUtil.getUUID()));
        }
        List<ESCIInfo> coverCiList = EamUtil.coverCiInfoList(ciList);
        List<Long> attrClassIds = Lists.newArrayList(attrClass.getCiClass().getId());
        Map<String, SavePrivateBatchCIContext> contextMap = ciPrivateSvc.saveOrUpdateBatchCI(coverCiList, attrClassIds, ownerCode, ownerCode);
        DataModelCiInfo result = new DataModelCiInfo();
        result.setAttributeDefs(attrClass.getAttrDefs());
        result.setAttributeCiClass(attrClass.getCiClass());
        //批量绑定关系
        List<BindCiRltRequestDto> bindList = new ArrayList<>();
        contextMap.forEach((eachCiCode, context) -> {
            BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(ownerCode).repetitionError(false).rltClassId(rltClassId)
                    .custom1("3").sourceCiId(entityId).targetCiId(context.getEsCi().getId()).build();
            bindList.add(bindRlt);
        });
        rltSwitchSvc.bindCiRltBatch(bindList, LibType.PRIVATE);
        List<CcCiInfo> resultCiList = new ArrayList<>();
        List<DataModelCiInfo> inheritCiList = new ArrayList<>();
        for (SavePrivateBatchCIContext each : contextMap.values()) {
            CcCiInfo resultCiInfo = EamUtil.coverESCIInfo(each.getEsCi(), EamUtil.copy(attrClass.getAttrDefs(), CcCiAttrDef.class), attrClass.getCiClass());
            resultCiList.add(resultCiInfo);
            String primaryKey = resultCiInfo.getAttrs().get(DataModelAttribute.PRIMARY_KEY);
            if(!BinaryUtils.isEmpty(primaryKey) && Boolean.parseBoolean(primaryKey)){
                DataModelCiInfo inheritCi = EamUtil.copy(resultCiInfo, DataModelCiInfo.class);
                inheritCiList.add(inheritCi);
            }
        }
        DataModelDiagramParam modelParam = getDiagramParam(attrCi.getDiagramId(), attrCi.getSheetId());
        Map<String, List<CcCiInfo>> inheritMap = new HashMap<>();
        if(CollectionUtils.isNotEmpty(inheritCiList)){
            inheritMap = inheritAttribute(entityCode, modelParam, inheritCiList);
        }
        updateInheritDiagramNode(modelParam.getModel(), inheritMap);
        inheritMap.put(entityCode, resultCiList);
        result.setInheritMap(inheritMap);
        return result;
    }

    @Override
    public List<CcCiRltInfo> getAttrByEntity(Long rltClassId, Long attrClassId, Set<String> entityCiCode, String ownerCode, LibType libType){
        ESRltSearchBean rltSearchBean = new ESRltSearchBean();
        rltSearchBean.setPageNum(1);
        rltSearchBean.setPageSize(1000);
        if(!BinaryUtils.isEmpty(ownerCode)){
            rltSearchBean.setOwnerCode(ownerCode);
        }
        rltSearchBean.setRltClassIds(Lists.newArrayList(rltClassId));
        rltSearchBean.setSourceCiCodes(Sets.newHashSet(entityCiCode));
        rltSearchBean.setTargetClassIds(Lists.newArrayList(attrClassId));
        Page<CcCiRltInfo> rltInfoPage = rltSwitchSvc.searchRltByBean(rltSearchBean, libType);
        if(BinaryUtils.isEmpty(rltInfoPage.getData())){
            return Collections.emptyList();
        }
        return rltInfoPage.getData();
    }

    /**
     * 获取实体中实体属性名称集合
     * @param entityNode 实体节点
     * @return 实体属性名称-关联实体集合
     */
    private DataModelAttributeParam getJsonName(ESDiagramNode entityNode){
        DataModelAttributeParam result = new DataModelAttributeParam();
        DiagramNodeJson entityJson = JSON.parseObject(entityNode.getNodeJson(), DiagramNodeJson.class);
        List<DataModelEntityNodeVo> attrList = entityJson.getItems();
        Map<String, DataModelEntityNodeVo> nameMap = new HashMap<>(attrList.size());
        Map<String, DataModelEntityNodeVo> inheritIdMap = new HashMap<>(attrList.size());
        for (DataModelEntityNodeVo node : attrList) {
            String name = node.getAttrs().get(DataModelAttribute.NAME_CN);
            if(BinaryUtils.isEmpty(name)){
                continue;
            }
            nameMap.put(name, node);
            String inheritId = node.getInheritId();
            if(!BinaryUtils.isEmpty(inheritId)){
                inheritIdMap.put(inheritId, node);
            }
        }
        result.setNameMap(nameMap);
        result.setInheritIdMap(inheritIdMap);
        return result;
    }

    @Override
    public Map<String, List<CcCiInfo>> inheritAttribute(String entityCiCode, DataModelDiagramParam modelParam, List<DataModelCiInfo> ciList){
        Map<String, List<CcCiInfo>> result = new HashMap<>(16);
        Map<String, DataModelDiagramNode> inheritMap = new HashMap<>(16);
        checkEntityRlt(modelParam, inheritMap, new HashSet<>(), entityCiCode);
        if(BinaryUtils.isEmpty(inheritMap)) {
            return result;
        }
        Long rltClassId = getDmRltClassId();
        List<ESCIInfo> nodeCiList = iciSwitchSvc.getCiByCodes(Lists.newArrayList(modelParam.getNodeMap().keySet()), modelParam.getOwnerCode(), LibType.PRIVATE);
        Map<String, ESCIInfo> nodeCiMap = nodeCiList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        //返回：实体code-属性ci
        Map<String, List<CcCiInfo>> entityAttrMap = new HashMap<>(16);
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        List<BindCiRltRequestDto> bindRltList = setAttribute(ciList, inheritMap.values(), nodeCiMap, entityAttrMap, rltClassId, attrClass);
        if(!BinaryUtils.isEmpty(bindRltList)){
            //再次更新实体属性
            List<CcCiInfo> copyCiList = EamUtil.copy(ciList, CcCiInfo.class);
            entityAttrMap.put(entityCiCode, copyCiList);
            List<CcCiInfo> attrCiList = entityAttrMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
            //通过业务主键去重
            Map<String, CcCiInfo> attrCiMap = attrCiList.stream().collect(Collectors.toMap(each -> each.getCi().getCiPrimaryKey(), each -> each, (k1, k2) -> k2));
            List<ESCIInfo> coverCiList = EamUtil.coverCiInfoList(Lists.newArrayList(attrCiMap.values()));
            Map<String, SavePrivateBatchCIContext> saveMap = ciPrivateSvc.saveOrUpdateBatchCI(coverCiList, Lists.newArrayList(attrClass.getCiClass().getId()), modelParam.getOwnerCode(), modelParam.getOwnerCode());
            Map<String, SavePrivateBatchCIContext> saveKeyMap = new HashMap<>(16);
            for (Map.Entry<String, SavePrivateBatchCIContext> each : saveMap.entrySet()) {
                SavePrivateBatchCIContext context = each.getValue();
                Map<String, Object> attrs = context.getEsCi().getAttrs();
                String attrName = attrs.get(DataModelAttribute.NAME_CN).toString();
                String entityCode = attrs.get(DataModelAttribute.ENTITY).toString();
                saveKeyMap.put(attrName + "-" + entityCode, context);
            }
            //返回结果排序处理
            List<String> nameList = ciList.stream().map(each -> each.getAttrs().get(DataModelAttribute.NAME_CN)).collect(Collectors.toList());
            entityAttrMap.forEach((ciCode, attrList) -> {
                List<CcCiInfo> resultAttrs = new ArrayList<>();
                for (CcCiInfo ci : attrList) {
                    String attrName = ci.getAttrs().get(DataModelAttribute.NAME_CN);
                    String entityCode = ci.getAttrs().get(DataModelAttribute.ENTITY);
                    SavePrivateBatchCIContext context = saveKeyMap.get(attrName + "-" + entityCode);
                    resultAttrs.add(EamUtil.coverESCIInfo(context.getEsCi(), null, null));
                }
                resultAttrs.sort(Comparator.comparing(each -> nameList.indexOf(each.getAttrs().get(DataModelAttribute.NAME_CN))));
                result.put(ciCode, resultAttrs);
            });
            for (BindCiRltRequestDto bind : bindRltList) {
                String targetKey = bind.getTargetKey();
                Long attrId = saveKeyMap.get(targetKey).getEsCi().getId();
                bind.setTargetKey(null);
                bind.setTargetCiId(attrId);
            }
            rltSwitchSvc.bindCiRltBatch(bindRltList, LibType.PRIVATE);
        }
        return result;
    }

    @Override
    public void updateInheritDiagramNode(ESDiagramModel model, Map<String, List<CcCiInfo>> entityAttrMap){
        if(BinaryUtils.isEmpty(entityAttrMap)){
            return;
        }
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("dEnergy.keyword", model.getdEnergy()));
        query.must(QueryBuilders.termQuery("sheetId.keyword", model.getSheetId()));
        query.must(QueryBuilders.termsQuery("ciCode.keyword", entityAttrMap.keySet()));
        List<ESDiagramNode> nodeList = nodeDao.getListByQuery(query);
        for (ESDiagramNode node : nodeList) {
            DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
            List<String> ciCodeList = nodeJson.getItems().stream().map(DataModelEntityNodeVo::getCiCode).collect(Collectors.toList());
            List<CcCiInfo> attrs = entityAttrMap.get(node.getCiCode());
            Map<String, CcCiInfo> attrMap = new HashMap<>(16);
            for (CcCiInfo each : attrs) {
                String inheritId = each.getAttrs().get(DataModelAttribute.INHERIT_ID);
                String ciCode = each.getCi().getCiCode();
                attrMap.put(ciCode, each);
                if(ciCodeList.contains(ciCode)){
                    continue;
                }
                DataModelEntityNodeVo attrNode = new DataModelEntityNodeVo();
                attrNode.setLabel(each.getAttrs().get(DataModelAttribute.NAME_CN));
                attrNode.setInheritId(inheritId);
                attrNode.setCiLabel(each.getCi().getCiLabel());
                attrNode.setCiCode(ciCode);
                attrNode.setClassId(each.getCi().getClassId());
                attrNode.setShape("实体ER图|实体属性");
                attrNode.setIsSelected(false);
                attrNode.setAttrs(each.getAttrs());
                nodeJson.getItems().add(attrNode);
            }
            //更新视图节点
            for (DataModelEntityNodeVo item : nodeJson.getItems()) {
                CcCiInfo attrCi = attrMap.get(item.getCiCode());
                if(BinaryUtils.isEmpty(attrCi)){
                    continue;
                }
                item.setInheritId(attrCi.getAttrs().get(DataModelAttribute.INHERIT_ID));
                item.setAttrs(attrCi.getAttrs());
                boolean primaryKey = Boolean.parseBoolean(attrCi.getAttrs().get(DataModelAttribute.PRIMARY_KEY));
                boolean foreignKey = Boolean.parseBoolean(attrCi.getAttrs().get(DataModelAttribute.FOREIGN_KEY));
                if(primaryKey && foreignKey){
                    item.setIcon("static/images/entityRelationship/keyPF.svg");
                } else if(foreignKey){
                    item.setIcon("static/images/entityRelationship/noKeyF.svg");
                } else if(primaryKey){
                    item.setIcon("static/images/entityRelationship/keyP.svg");
                } else if(!BinaryUtils.isEmpty(item.getAttrs().get(DataModelAttribute.STANDARD))){
                    item.setIcon("static/images/entityRelationship/StandardProp.svg");
                } else {
                    item.setIcon("static/images/entityRelationship/SelfProp.svg");
                }
            }
            node.setNodeJson(JSON.toJSONString(nodeJson));
        }
        nodeDao.saveOrUpdateBatch(nodeList);
    }

    @Override
    public DataModelCiInfo saveOrUpdateEntityBatch(DealAttributeDto dto) {
        String ownerCode = dto.getOwnerCode();
        Long classId = dto.getCiList().get(0).getCi().getClassId();
        ESCIClassInfo classInfo = ciClassApiSvc.queryClassById(classId);
        if(BinaryUtils.isEmpty(classInfo)){
            throw new ServerException("对象分类不存在或已删除,请更新制品信息!");
        }
        String classCode = classInfo.getClassCode();
        //新建或从标准库拖
        if(!Env.LOGIC_ENTITY.getCode().equals(classCode) && !Env.CONCEPTION_ENTITY.getCode().equals(classCode)
                && !Env.PHYSICAL_ENTITY.getCode().equals(classCode)){
            throw new ServerException("保存失败,不是实体对象!");
        }
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        DataModelCiInfo result = new DataModelCiInfo();
        List<CcCiInfo> ciList = saveEntityBatch(dto, classInfo);
        result.setCiList(ciList);
        result.setCiClass(classInfo);
        result.setAttrDefs(attrClass.getAttrDefs());
        return result;
    }

    @Override
    public AttrAndCiDto quickDrawingEr(String diagramId, LibType libType) {
        ESDiagram esDiagram = diagramApiClient.getEsDiagram(diagramId, 0);
        String loginCode = esDiagram.getOwnerCode();
        Long artifactId = Long.valueOf(esDiagram.getViewType());
        EamArtifactVo eamArtifactVo = artifactSvc.queryArtifact(artifactId);
        Integer type = eamArtifactVo.getTypeClassification();
        String classCode;
        if (type == 6) {
            classCode = "ConceptionEntity";
        } else if (type == 7 || type == 11) {
            classCode = "LogicEntity";
        } else if (type == 12) {
            classCode = "PhysicalEntity";
        } else {
            throw new BinaryException("当前视图绑定的制品类型非er图制品类型，请检查");
        }
        List<Long> classIdList = artifactSvc.getArtifactClassIds(artifactId);
        if (BinaryUtils.isEmpty(classIdList)) {
            return null;
        }
        //因为制品存量数据没有classCode字段，所以只能把id取出来再查询一次进行筛选
        List<ESCIClassInfo> classInfoList = ciClassApiSvc.selectCiClassByIds(classIdList);
        List<ESCIClassInfo> entityList = classInfoList.stream().filter(e -> classCode.equals(e.getClassCode())).distinct().collect(Collectors.toList());
        if (BinaryUtils.isEmpty(entityList)) {
            return null;
        }
        ESCIClassInfo classInfo = entityList.get(0);
        //拿到分类中配置的属性名称，把绑定视图的ciCode对应的属性值查出来，去分类中查
        List<ESCIAttrDefInfo> attrDefs = classInfo.getAttrDefs();
        List<ESCIAttrDefInfo> attrDefInfos = attrDefs.stream().filter(each -> each.getProName().equals("所属主题域"))
                .filter(each -> !BinaryUtils.isEmpty(each.getProDropSourceDef()))
                .collect(Collectors.toList());
        if (BinaryUtils.isEmpty(attrDefInfos)) {
            return null;
        }
        ESCIAttrDefInfo esciAttrDefInfo = attrDefInfos.get(0);
        Long classId = CiClassProDropSourceDefHelper.getCiClassProDropSourceClassId(esciAttrDefInfo.getProDropSourceDef().trim());
        Long[] attrDefIds = CiClassProDropSourceDefHelper.getCiClassProDropSourceDefIds(esciAttrDefInfo.getProDropSourceDef().trim());
        List<ESCIClassInfo> defList = classSvc.getAllDefESClassInfosByClassIds(SysUtil.getCurrentUserInfo().getDomainId(), Collections.singleton(classId));
        String proName = null;
        if (!BinaryUtils.isEmpty(defList)) {
            List<ESCIAttrDefInfo> defInfos = defList.get(0).getAttrDefs().stream().filter(each -> each.getId().equals(attrDefIds[0])).collect(Collectors.toList());
            proName = defInfos.get(0).getProName();
        }
        EamDiagramRelationSys relationSys = sysService.getEamDiagramRelationSys(diagramId);
        if (BinaryUtils.isEmpty(relationSys)) {
            return null;
        }
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByCodes(Collections.singletonList(relationSys.getEsSysId()), loginCode, libType);
        if (BinaryUtils.isEmpty(ciList)) {
            return null;
        }
        Object attrName = ciList.get(0).getAttrs().get(proName);
        if (BinaryUtils.isEmpty(attrName)) {
            return null;
        }
        String value = attrName.toString();
        ESCISearchBean bean = new ESCISearchBean();
        bean.setClassIds(Collections.singletonList(classInfo.getId()));
        bean.setOwnerCode(loginCode);
        bean.setAndAttrs(this.getEsAttrBeans(type, value));
        List<ESCIInfo> entityCiList = iciSwitchSvc.searchESCIByBean(bean, libType).getData();
        if (BinaryUtils.isEmpty(entityCiList)) {
            return null;
        }
        Map<String, ESCIInfo> entityMap = entityCiList.stream().collect(Collectors.toMap(ESCIInfo::getCiCode, each -> each, (k1, k2) -> k1));
        // 查询属性数据;
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        Map<String, List<CcCiInfo>> attrMap = modelQuerySvc.queryEntityAttrs(entityCiList, classInfo.getId(), attrClass.getCiClass().getId(), loginCode, libType);
        AttrAndCiDto result = new AttrAndCiDto();
        result.setEntityAttrDefs(classInfo.getCcAttrDefs());
        List<EntityAndAttributeVo> entityAndAttributeVos = new ArrayList<>();
        entityCiList.sort(Comparator.comparing(CcCi::getCiLabel).thenComparing(CcCi::getCiPrimaryKey));
        for (ESCIInfo entity : entityCiList) {
            String entityCiCode = entity.getCiCode();
            EntityAndAttributeVo attrAndCiDto = new EntityAndAttributeVo();
            ESCIInfo entityInfo = entityMap.get(entityCiCode);
            entityInfo.setShape(classInfo.getShape());
            attrAndCiDto.setEntityCi(entityInfo);
            List<CcCiInfo> attrCcCiList = attrMap.get(entityCiCode);
            List<ESCIInfo> attrList = new ArrayList<>();
            if (!BinaryUtils.isEmpty(attrCcCiList)) {
                for (CcCiInfo attrCiInfo : attrCcCiList) {
                    ESCIInfo esCi = commSvc.tranESCIInfo(attrCiInfo);
                    esCi.setShape(attrClass.getCiClass().getShape());
                    attrList.add(esCi);
                }
                attrAndCiDto.setAttrCiList(attrList);
                result.setAttrAttrDefs(attrClass.getAttrDefs());
            }
            entityAndAttributeVos.add(attrAndCiDto);
        }
        result.setEntityAndAttributeVos(entityAndAttributeVos);
        return result;
    }

    private List<ESAttrBean> getEsAttrBeans(Integer type, String value) {
        List<ESAttrBean> andAttrs = new ArrayList<>();
        ESAttrBean attrBeanOne = new ESAttrBean();
        if (type == 7) {
            attrBeanOne.setKey(DataModelEntity.ENTITY_TYPE);
            attrBeanOne.setOptType(1);
            attrBeanOne.setValue(LOGIC);
            andAttrs.add(attrBeanOne);
        }
        if (type == 11) {
            attrBeanOne.setKey(DataModelEntity.ENTITY_TYPE);
            attrBeanOne.setOptType(1);
            attrBeanOne.setValue(SYSTEM);
            andAttrs.add(attrBeanOne);
        }
        ESAttrBean attrBeanTwo = new ESAttrBean();
        attrBeanTwo.setKey(DataModelEntity.BELONG_SUBJECT);
        attrBeanTwo.setValue(value);
        attrBeanTwo.setOptType(1);
        andAttrs.add(attrBeanTwo);
        return andAttrs;
    }

    private String getClassCodeByArtifact(int type){
        switch (type){
            case 6: return Env.CONCEPTION_ENTITY.getCode();
            case 7:
            case 11: return Env.LOGIC_ENTITY.getCode();
            case 12: return Env.PHYSICAL_ENTITY.getCode();
            default: return null;
        }
    }

    private Map<String, ESCIInfo> queryNameMap(Long classId, String checkCode, String ownerCode){
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        CCcCi ccCi = new CCcCi();
        ccCi.setDomainId(domainId);
        ccCi.setClassId(classId);
        ccCi.setOwnerCodeEqual(ownerCode);
        List<ESCIInfo> data = iciSwitchSvc.queryESCIInfoList(domainId, ccCi, "", true, LibType.PRIVATE);
        Map<String, ESCIInfo> nameMap = new HashMap<>(16);
        if(!BinaryUtils.isEmpty(data)){
            for (ESCIInfo each : data) {
                Object name = each.getAttrs().get(DataModelEntity.NAME_CN);
                if(BinaryUtils.isEmpty(name)){
                    continue;
                }
                //对于逻辑实体,需要添加type作为唯一标识
                nameMap.put(name + checkCode, each);
            }
        }
        return nameMap;
    }

    private Long setEntityName(Map<String, ESCIInfo> nameMap, CcCiInfo ciInfo, String checkCode, LibType libType){
        //根据名称查询私有库是否有相同名称实体属性
        String name = ciInfo.getAttrs().get(DataModelEntity.NAME_CN);
        if(!BinaryUtils.isEmpty(name)){
            //根据名称查询私有库是否有相同名称实体属性
            ESCIInfo checkCi = nameMap.get(name + checkCode);
            if(!BinaryUtils.isEmpty(checkCi) && !checkCi.getCiCode().equals(ciInfo.getCi().getCiCode())){
                if(LibType.PRIVATE.equals(libType)){
                    throw new ServerException("私有库已存在相同名称实体,请从私有库拖出!");
                }else{
                    //使用私有库的ciId及ciCode
//                    iciSwitchSvc.removeById(checkCi.getId(), null, LibType.PRIVATE);
                    ciInfo.getCi().setId(null);
                    return checkCi.getId();
                }
            }
        } else {
            //设置实体默认名,设计库需要判断是否copy
            int num = 1;
            while (!BinaryUtils.isEmpty(nameMap.get("实体" + "-" + num + checkCode))){
                num++;
            }
            name = "实体" + "-" + num;
            ciInfo.getAttrs().put(DataModelEntity.NAME_CN, name);
            ciInfo.getAttrs().put(DataModelEntity.NAME_EN, "Entity" + "-" + num);
        }
        return null;
    }

    private List<CcCiInfo> saveEntityBatch(DealAttributeDto dto, ESCIClassInfo classInfo){
        List<CcCiInfo> result = new ArrayList<>();
        //判断视图类型
        DataModelDiagramParam modelParam = getDiagramParam(dto.getDiagramId(), dto.getSheetId());
        long viewType = Long.parseLong(modelParam.getDiagram().getViewType());
        EamArtifactVo artifact = artifactSvc.queryArtifact(viewType);
        Assert.notNull(artifact, "制品类型未发布或已删除!");
        String classCode = getClassCodeByArtifact(artifact.getTypeClassification());
        String type = null;
        //逻辑实体
        if(Env.LOGIC_ENTITY.getCode().equals(classCode)){
            type = viewType == 7L?LOGIC:SYSTEM;
        }
        String checkCode = type == null?"" : "-" + type;
        String ownerCode = dto.getOwnerCode();
        Map<String, ESCIInfo> nameMap = queryNameMap(classInfo.getId(), checkCode, ownerCode);
        //设计库-私有库ciCode映射
        Map<String, String> ciCodeMap = new HashMap<>();
        List<ESCIInfo> saveList = new ArrayList<>();
        List<Long> removeIds = new ArrayList<>();
        for (CcCiInfo ciInfo : dto.getCiList()) {
            String oldCiCode = ciInfo.getCi().getCiCode();
            Long removeId = setEntityName(nameMap, ciInfo, checkCode, dto.getLibType());
            if(removeId != null){
                removeIds.add(removeId);
            }
            ciInfo.getCi().setOwnerCode(ownerCode);
            if(type != null){
                ciInfo.getAttrs().put(DataModelEntity.ENTITY_TYPE, type);
            }
            String name = ciInfo.getAttrs().get(DataModelEntity.NAME_CN);
            ESCIInfo covered = EamUtil.coverCiInfo(ciInfo);
            saveList.add(covered);
            nameMap.put(name + checkCode, covered);
            ciCodeMap.put(oldCiCode, covered.getCiCode());
        }
        iciSwitchSvc.removeByIds(removeIds, null, LibType.PRIVATE);
        List<Long> entityClassId = Collections.singletonList(classInfo.getId());
        Map<String, SavePrivateBatchCIContext> ciContextMap = ciPrivateSvc.saveOrUpdateBatchCI(saveList, entityClassId, ownerCode, ownerCode);
        Map<String, Long> entityMap = new HashMap<>();
        for (Map.Entry<String, SavePrivateBatchCIContext> entry : ciContextMap.entrySet()) {
            SavePrivateBatchCIContext context = entry.getValue();
            CcCiInfo ciInfo = EamUtil.coverESCIInfo(context.getEsCi(), EamUtil.copy(classInfo.getAttrDefs(), CcCiAttrDef.class), classInfo);
            ciInfo.getCi().setShape(classInfo.getShape());
            result.add(ciInfo);
            entityMap.put(ciInfo.getCi().getCiCode(), ciInfo.getCi().getId());
        }
        if(LibType.DESIGN.equals(dto.getLibType())){
            copyDesignAttr(ciCodeMap, entityMap, ownerCode);
        }
        return result;
    }

    /**
     * 批量复制实体属性
     * @param ciCodeMap 设计库-私有库ciCode映射
     * @param entityMap 实体<实体ciCode,实体ciId>
     * @param ownerCode 用户标识
     */
    private void copyDesignAttr(Map<String, String> ciCodeMap, Map<String, Long> entityMap, String ownerCode){
        try {
            Long rltClassId = getDmRltClassId();
            CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
            //批量绑定关系
            List<BindCiRltRequestDto> bindList = new ArrayList<>();
            List<CcCiRltInfo> allRltList = getAttrByEntity(rltClassId, attrClass.getCiClass().getId(), ciCodeMap.keySet(), null, LibType.DESIGN);
            if(CollectionUtils.isEmpty(allRltList)){
                return;
            }
            Map<String, List<CcCiRltInfo>> entityRltMap = allRltList.stream().collect(Collectors.groupingBy(each -> each.getSourceCiInfo().getCi().getCiCode()));
            for (Map.Entry<String, String> entry : ciCodeMap.entrySet()) {
                List<CcCiRltInfo> rltList = entityRltMap.get(entry.getKey());
                if(BinaryUtils.isEmpty(rltList)){
                    continue;
                }
                List<CcCiInfo> attrList = rltList.stream().map(CcCiRltInfo::getTargetCiInfo).collect(Collectors.toList());
                //copy实体属性到私有库
                List<ESCIInfo> coverCiList = new ArrayList<>();
                for (CcCiInfo ciInfo : attrList) {
                    ESCIInfo each = EamUtil.coverCiInfo(ciInfo);
                    each.setId(null);
                    each.setOwnerCode(ownerCode);
                    each.getAttrs().put(DataModelAttribute.INHERIT_ID, ESUtil.getUUID());
                    each.getAttrs().put(DataModelAttribute.ENTITY, entry.getValue());
                    coverCiList.add(each);
                }
                Long ciId = entityMap.get(entry.getValue());
                List<Long> attrClassId = Collections.singletonList(attrClass.getCiClass().getId());
                Map<String, SavePrivateBatchCIContext> contextMap = ciPrivateSvc.saveOrUpdateBatchCI(coverCiList, attrClassId, ownerCode, ownerCode);
                contextMap.forEach((eachCiCode, context) -> {
                    BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(ownerCode).repetitionError(false).rltClassId(rltClassId)
                            .custom1("3").sourceCiId(ciId).targetCiId(context.getEsCi().getId()).build();
                    bindList.add(bindRlt);
                });
            }
            rltSwitchSvc.bindCiRltBatch(bindList, LibType.PRIVATE);
        }catch (Exception e){
            log.error("实体属性处理失败!");
        }
    }

    /**
     * 由于前端接收到继承节点后，无法及时更新视图继承节点数据库信息，刷新页面会出现继承节点数据未保存问题
     * 此处直接删除视图节点 实体 继承到的实体属性信息
     * @param model 视图model信息
     * @param entityDelMap 视图实体节点删除属性map
     */
    private void updateDelDiagramNode(ESDiagramModel model, Map<String, List<String>> entityDelMap){
        BoolQueryBuilder query = QueryBuilders.boolQuery();
        query.must(QueryBuilders.termQuery("dEnergy.keyword", model.getdEnergy()));
        query.must(QueryBuilders.termQuery("sheetId.keyword", model.getSheetId()));
        query.must(QueryBuilders.termsQuery("ciCode.keyword", entityDelMap.keySet()));
        List<ESDiagramNode> nodeList = nodeDao.getListByQuery(query);
        for (ESDiagramNode node : nodeList) {
            List<String> delCodes = entityDelMap.get(node.getCiCode());
            DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
            List<DataModelEntityNodeVo> delAttr = nodeJson.getItems().stream().filter(each -> delCodes.contains(each.getCiCode())).collect(Collectors.toList());
            nodeJson.getItems().removeAll(delAttr);
            node.setNodeJson(JSON.toJSONString(nodeJson));
        }
        nodeDao.saveOrUpdateBatch(nodeList);
    }

    /**
     * 1.整理每个实体节点应该继承的实体属性集合
     * 2.继承id字段:每个继承实体属性生成一个关联id
     * @param ciList 继承的实体属性集合
     * @param inheritList 视图需要继承属性的节点集合
     * @param nodeCiMap 实体ci集合
     * @param entityAttrMap 继承节点实体ciCode-继承实体属性集合
     * @param rltClassId 关系分类id
     * @return 应绑定关系集合
     */
    private List<BindCiRltRequestDto> setAttribute(List<DataModelCiInfo> ciList, Collection<DataModelDiagramNode> inheritList, Map<String,
                                                    ESCIInfo> nodeCiMap, Map<String, List<CcCiInfo>> entityAttrMap, Long rltClassId, CcCiClassInfo attrClass){
        List<BindCiRltRequestDto> bindRltList = new ArrayList<>();
        Map<String, DataModelAttributeParam> attributeParamMap = new HashMap<>(inheritList.size());
        for (DataModelDiagramNode node : inheritList) {
            DataModelAttributeParam attributeParam = getJsonName(node);
            attributeParamMap.put(node.getCiCode(), attributeParam);
        }
        for (DataModelCiInfo ciInfo : ciList) {
            String name = ciInfo.getAttrs().get(DataModelAttribute.NAME_CN);
            String inheritId = ciInfo.getAttrs().get(DataModelAttribute.INHERIT_ID);
            String ownerCode = ciInfo.getCi().getOwnerCode();
            if(BinaryUtils.isEmpty(inheritId)){
                inheritId = Long.toString(ESUtil.getUUID());
                ciInfo.getAttrs().put(DataModelAttribute.INHERIT_ID, inheritId);
            }
            List<String> ciPKAttrDefNames = CommUtil.getCiPKAttrDefNames(attrClass.getAttrDefs());
            String classStdCode = attrClass.getCiClass().getClassStdCode();
            for (DataModelDiagramNode inheritNode : inheritList) {
                DataModelAttributeParam attributeParam = attributeParamMap.get(inheritNode.getCiCode());
                DataModelCodeNameVo inheritVo = checkName(attributeParam, name, inheritId, ciInfo);
                //将当前实体继承属性名称加入map,否则下次校验名称重复会遗漏
                attributeParam.getNameMap().put(inheritVo.getName(), new DataModelEntityNodeVo());
                CcCiInfo inheritCi = EamUtil.copy(ciInfo, CcCiInfo.class);
                CcCi ci = EamUtil.copy(ciInfo.getCi(), CcCi.class);
                ci.setId(null);
                String ciCode = BinaryUtils.isEmpty(inheritVo.getCiCode())?String.valueOf(ESUtil.getUUID()):inheritVo.getCiCode();
                ci.setCiCode(ciCode);
                Map<String, String> attrs = new HashMap<>(ciInfo.getAttrs());
                attrs.put(DataModelAttribute.NAME_CN, inheritVo.getName());
                if(!BinaryUtils.isEmpty(inheritVo.getEnName())){
                    attrs.put(DataModelAttribute.NAME_EN, inheritVo.getEnName());
                }
                attrs.put(DataModelAttribute.ENTITY, inheritNode.getCiCode());
                attrs.put(DataModelAttribute.INHERIT_ID, inheritId);
                attrs.put(DataModelAttribute.FOREIGN_KEY, Boolean.toString(true));
                if(BinaryUtils.isEmpty(inheritVo.getCiCode())){
                    attrs.put(DataModelAttribute.PRIMARY_KEY, Boolean.toString(inheritNode.getPrimaryKey()));
                }else{
                    attrs.put(DataModelAttribute.PRIMARY_KEY, Boolean.toString(inheritVo.getPrimaryKey()));
                }
                List<String> ciPrimaryKeys = CommUtil.getCiPrimaryKeys(classStdCode, attrs, ciPKAttrDefNames);
                ci.setCiPrimaryKey(JSON.toJSONString(ciPrimaryKeys));
                inheritCi.setCi(ci);
                inheritCi.setAttrs(attrs);
                entityAttrMap.putIfAbsent(inheritNode.getCiCode(), new ArrayList<>());
                entityAttrMap.get(inheritNode.getCiCode()).add(inheritCi);
                if(BinaryUtils.isEmpty(nodeCiMap.get(inheritNode.getCiCode()))){
                    continue;
                }
                BindCiRltRequestDto reqBean = BindCiRltRequestDto.builder().sourceCiId(nodeCiMap.get(inheritNode.getCiCode()).getId())
                        .targetKey(inheritVo.getName() + "-" + inheritNode.getCiCode()).ownerCode(ownerCode).rltClassId(rltClassId).build();
                bindRltList.add(reqBean);
            }
        }
        return bindRltList;
    }

    /**
     * 校验名称是否重复,重复则加-num
     * @param attributeParam 名称集合名称-关联实体(实体code-实体属性code)
     * @param name 名称
     * @param inheritId 继承标识
     * @param ciInfo 源实体属性
     * @return 名称
     */
    private DataModelCodeNameVo checkName(DataModelAttributeParam attributeParam, String name, String inheritId, DataModelCiInfo ciInfo){
        Map<String, DataModelEntityNodeVo> nameMap = attributeParam.getNameMap();
        Map<String, DataModelEntityNodeVo> inheritIdMap = attributeParam.getInheritIdMap();
        DataModelEntityNodeVo node = inheritIdMap.get(inheritId);
        if(BinaryUtils.isEmpty(node)){
            if(BinaryUtils.isEmpty(nameMap.get(name))){
                return new DataModelCodeNameVo(name, null);
            }
            String prefix = name;
            int num = 1;
            if(name.contains("-")){
                String suffix = name.substring(name.lastIndexOf("-")+1);
                if(!BinaryUtils.isEmpty(suffix) && NumberUtils.isInteger(suffix)){
                    prefix = name.substring(0, name.lastIndexOf("-"));
                }
            }
            while (!BinaryUtils.isEmpty(nameMap.get(prefix + "-" + num))){
                num++;
            }
            return new DataModelCodeNameVo(prefix + "-" + num, null);
        }
        String newEnName = ciInfo.getAttrs().get(DataModelAttribute.NAME_EN);
        String cnName = node.getAttrs().get(DataModelAttribute.NAME_CN);
        String enName = node.getAttrs().get(DataModelAttribute.NAME_EN);
        Boolean primaryKey = Boolean.parseBoolean(node.getAttrs().get(DataModelAttribute.PRIMARY_KEY));
        DataModelCodeNameVo result = new DataModelCodeNameVo(name, newEnName, primaryKey, node.getCiCode());
        if((!BinaryUtils.isEmpty(ciInfo.getOldCnName()) && !cnName.equals(ciInfo.getOldCnName())) || (BinaryUtils.isEmpty(ciInfo.getOldCnName()) && !cnName.equals(name))){
            result.setName(cnName);
        }
        if((!BinaryUtils.isEmpty(ciInfo.getOldEnName()) && !enName.equals(ciInfo.getOldEnName())) || (BinaryUtils.isEmpty(ciInfo.getOldEnName()) && !enName.equals(newEnName))){
            result.setEnName(enName);
        }
        return result;
    }

    /**
     * 递归解析视图节点及关系线，拿出从当前节点出发的包含多对多及父子关系的实体，做属性继承处理
     * @param modelParam 视图参数
     * @param inheritMap 存在一对一关系、父子关系及一对多关系并需要继承属性的视图节点集合
     * @param filter 用于过滤关系线，避免收尾相接的情况
     * @param ciCode 源端实体ciCode
     */
    private void checkEntityRlt(DataModelDiagramParam modelParam, Map<String, DataModelDiagramNode> inheritMap, Set<ESDiagramLink> filter, String ciCode){
        ESDiagramNode entityNode = modelParam.getNodeMap().get(ciCode);
        List<ESDiagramLink> linkList = modelParam.getFromLinkGroup().get(entityNode.getKey());
        if(BinaryUtils.isEmpty(linkList)){
            return;
        }
        for (ESDiagramLink link : linkList) {
            //判断关系线类型
            JSONObject linkJson = JSON.parseObject(link.getLinkJson());
            if(BinaryUtils.isEmpty(linkJson.get("className")) || BinaryUtils.isEmpty(linkJson.get("to"))){
                continue;
            }
            String className = linkJson.get("className").toString();
            String toKey = linkJson.get("to").toString();
            String fromKey = linkJson.get("from").toString();
            ESDiagramNode toNode = modelParam.getKeyMap().get(toKey);
            if(fromKey.equals(toKey) || BinaryUtils.isEmpty(toNode) || BinaryUtils.isEmpty(toNode.getCiCode()) || filter.contains(link)){
                continue;
            }
            DataModelDiagramNode node = EamUtil.copy(toNode, DataModelDiagramNode.class);
            if(Env.ONE_TO_ONE.equals(className) || Env.PATERNITY.equals(className)){
                inheritMap.put(node.getKey(), node);
                filter.add(link);
                checkEntityRlt(modelParam, inheritMap, filter, node.getCiCode());
            }else if(Env.ONE_TO_MANY.equals(className)) {
                node.setPrimaryKey(false);
                inheritMap.put(node.getKey(), node);
                filter.add(link);
            }
        }
    }

    /**
     * 数据标准对象转换为实体属性对象
     * @param classId 实体属性分类id
     * @param ciInfo 数据标准对象
     */
    private void coverStandardToAttribute(Long classId, CcCiInfo ciInfo){
        String standardCiCode = ciInfo.getCi().getCiCode();
        Map<String, String> attrs = ciInfo.getAttrs();
        Map<String, String> newAttrs = new HashMap<>(16);
        newAttrs.put(DataModelAttribute.ENTITY, attrs.get(DataModelAttribute.ENTITY));
        newAttrs.put(DataModelAttribute.NAME_CN, attrs.get(DataModelAttribute.NAME_CN));
        newAttrs.put(DataModelAttribute.NAME_EN, attrs.get(DataModelAttribute.NAME_EN));
        newAttrs.put(DataModelAttribute.DATA_TYPE, attrs.get(DataModelAttribute.DATA_TYPE));
        newAttrs.put(DataModelAttribute.STANDARD, standardCiCode);
        newAttrs.put(DataModelAttribute.VAL_DOMAIN, attrs.get(DataModelAttribute.VAL_DOMAIN));
        newAttrs.put(DataModelAttribute.PRIMARY_KEY, attrs.get(DataModelAttribute.PRIMARY_KEY));
        newAttrs.put(DataModelAttribute.NON_EMPTY, attrs.get(DataModelAttribute.NON_EMPTY));
        newAttrs.put(DataModelAttribute.DEFINITION, attrs.get(DataModelAttribute.DEFINITION));
        newAttrs.put(DataModelAttribute.LENGTH, attrs.get(DataModelAttribute.LENGTH));
        newAttrs.put(DataModelAttribute.PRECISION, attrs.get(DataModelAttribute.PRECISION));
        ciInfo.getCi().setId(null);
        ciInfo.getCi().setCiCode(null);
        ciInfo.getCi().setClassId(classId);
        ciInfo.setAttrs(newAttrs);
    }

    @Override
    public synchronized DataModelBatchResp dealAttributeBatch(DealAttributeDto dto) {
        List<CcCiInfo> attrList = dto.getCiList();
        DataModelBatchResp result = new DataModelBatchResp();
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        result.setAttributeDefs(attrClass.getAttrDefs());
        result.setAttributeCiClass(attrClass.getCiClass());
        if(BinaryUtils.isEmpty(attrList)){
            return result;
        }
        Long attrClassId = attrClass.getCiClass().getId();
        //查询实体分类及实体属性分类
        List<ESCIInfo> entityList = iciSwitchSvc.getCiByCodes(Lists.newArrayList(dto.getCiCode()), dto.getOwnerCode(), LibType.PRIVATE);
        if(BinaryUtils.isEmpty(entityList)){
            throw new ServerException("未查询到当前实体信息!");
        }
        ESCIInfo entity = entityList.get(0);
        Long rltClassId = getDmRltClassId();
        Map<String, CcCiInfo> currAttrMap = new HashMap<>(attrList.size());
        Set<String> nameFilter = new HashSet<>();
        for (CcCiInfo each : attrList) {
            String ciCode = each.getCi().getCiCode();
            String name = each.getAttrs().get(DataModelAttribute.NAME_CN);
            if(BinaryUtils.isEmpty(name)){
                throw new ServerException("实体属性'中文名称'字段不能为空!");
            }
            if(nameFilter.contains(name)){
                throw new ServerException("实体属性'中文名称'字段不能重复!");
            }
            nameFilter.add(name);
            if(!BinaryUtils.isEmpty(ciCode)){
                currAttrMap.put(ciCode, each);
            } else {
                each.getCi().setCiCode(Long.toString(ESUtil.getUUID()));
                each.getCi().setClassId(attrClassId);
                each.getCi().setOwnerCode(dto.getOwnerCode());
                each.getAttrs().put(DataModelAttribute.INHERIT_ID, Long.toString(ESUtil.getUUID()));
            }
            each.getAttrs().put(DataModelAttribute.ENTITY, dto.getCiCode());
        }
        //删除
        DataModelDiagramParam modelParam = getDiagramParam(dto.getDiagramId(), dto.getSheetId());
        ESDiagramNode entityNode = modelParam.getNodeMap().get(dto.getCiCode());
        List<ESDiagramLink> toLinks = modelParam.getToLinkGroup().get(entityNode.getKey());
        List<String> delAttrCodes = new ArrayList<>();
        List<String> inheritIds = new ArrayList<>();
        List<String> diagramAttrCodes = new ArrayList<>();
        DiagramNodeJson entityNodeJson = JSON.parseObject(entityNode.getNodeJson(), DiagramNodeJson.class);
        for (DataModelEntityNodeVo item : entityNodeJson.getItems()) {
            if(BinaryUtils.isEmpty(item.getCiCode())){
                continue;
            }
            diagramAttrCodes.add(item.getCiCode());
            boolean primaryKey = Boolean.parseBoolean(item.getAttrs().get(DataModelAttribute.PRIMARY_KEY));
            boolean foreignKey = Boolean.parseBoolean(item.getAttrs().get(DataModelAttribute.FOREIGN_KEY));
            CcCiInfo currCiInfo = currAttrMap.get(item.getCiCode());
            if(BinaryUtils.isEmpty(currCiInfo)){
                //不存在判断是否可以删除，及是否需要做级联删除处理
                if(!BinaryUtils.isEmpty(toLinks) && foreignKey){
                    throw new ServerException("所选字段有外键依赖无法删除,请在父表的主键中删除!");
                }
                if(primaryKey && !BinaryUtils.isEmpty(item.getInheritId())){
                    inheritIds.add(item.getInheritId());
                }
                delAttrCodes.add(item.getCiCode());
            }else{
                //还存在判断是否更新了主键或外键信息:主键改非主键,也做级联删除
                boolean currPrimaryKey = Boolean.parseBoolean(currCiInfo.getAttrs().get(DataModelAttribute.PRIMARY_KEY));
                if(primaryKey && !currPrimaryKey && !BinaryUtils.isEmpty(item.getInheritId())){
                    inheritIds.add(item.getInheritId());
                }
            }
        }
        List<ESCIInfo> diagramCiList = iciSwitchSvc.getCiByCodes(diagramAttrCodes, dto.getOwnerCode(), LibType.PRIVATE);
        Map<String, ESCIInfo> diagramCiMap = diagramCiList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        if(!BinaryUtils.isEmpty(inheritIds)){
            Map<String, List<String>> delMap = delPrimaryKey(dto.getCiCode(), inheritIds, modelParam);
            result.setDelMap(delMap);
        }
        //加入当前实体要删除的实体属性
        if(!BinaryUtils.isEmpty(delAttrCodes)){
            result.getDelMap().putIfAbsent(dto.getCiCode(), Lists.newArrayList());
            result.getDelMap().put(dto.getCiCode(), delAttrCodes);
            List<ESCIInfo> delCiList = diagramCiList.stream().filter(each -> delAttrCodes.contains(each.getCiCode())).collect(Collectors.toList());
            List<Long> attrDelIds = delCiList.stream().map(CcCi::getId).collect(Collectors.toList());
            iciSwitchSvc.removeByIds(attrDelIds, null, LibType.PRIVATE);
        }
        List<ESCIInfo> coverCiList = EamUtil.coverCiInfoList(attrList);
        List<Long> attrClassIds = Lists.newArrayList(attrClassId);
        Map<String, SavePrivateBatchCIContext> contextMap = ciPrivateSvc.saveOrUpdateBatchCI(coverCiList, attrClassIds, dto.getOwnerCode(), dto.getOwnerCode());

        List<BindCiRltRequestDto> bindList = new ArrayList<>();
        List<CcCiInfo> currEntityAttrList = new ArrayList<>();
        List<DataModelCiInfo> primaryCiList = new ArrayList<>();
        for (Map.Entry<String, SavePrivateBatchCIContext> each : contextMap.entrySet()) {
            CcCiInfo eachCi = EamUtil.coverESCIInfo(each.getValue().getEsCi(), null, null);
            currEntityAttrList.add(eachCi);
            boolean primaryKey = Boolean.parseBoolean(eachCi.getAttrs().get(DataModelAttribute.PRIMARY_KEY));
            if(primaryKey){
                DataModelCiInfo copy = EamUtil.copy(eachCi, DataModelCiInfo.class);
                ESCIInfo oldCi = diagramCiMap.get(copy.getCi().getCiCode());
                if(!BinaryUtils.isEmpty(oldCi)){
                    String newCnName = copy.getAttrs().get(DataModelAttribute.NAME_CN);
                    String oldCnName = oldCi.getAttrs().get(DataModelAttribute.NAME_CN).toString();
                    if(!oldCnName.equals(newCnName)){
                        copy.setOldCnName(oldCnName);
                    }
                    String newEnName = copy.getAttrs().get(DataModelAttribute.NAME_EN);
                    Object oldEnObj = oldCi.getAttrs().get(DataModelAttribute.NAME_EN);
                    if(!BinaryUtils.isEmpty(oldEnObj) && !oldEnObj.toString().equals(newEnName)){
                        copy.setOldEnName(oldEnObj.toString());
                    }
                }
                primaryCiList.add(copy);
            }
            BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(dto.getOwnerCode()).repetitionError(false).rltClassId(rltClassId)
                    .custom1("3").sourceCiId(entity.getId()).targetCiId(eachCi.getCi().getId()).build();
            bindList.add(bindRlt);
        }
        rltSwitchSvc.bindCiRltBatch(bindList, LibType.PRIVATE);
        Map<String, List<CcCiInfo>> inheritMap = new HashMap<>();
        if(CollectionUtils.isNotEmpty(primaryCiList)){
            //做属性继承处理
            inheritMap = inheritAttribute(dto.getCiCode(), modelParam, primaryCiList);
        }
        //返回结果排序处理
        List<String> nameList = attrList.stream().map(each -> each.getAttrs().get(DataModelAttribute.NAME_CN)).collect(Collectors.toList());
        currEntityAttrList.sort(Comparator.comparing(each -> nameList.indexOf(each.getAttrs().get(DataModelAttribute.NAME_CN))));
        inheritMap.put(dto.getCiCode(), currEntityAttrList);
        updateInheritDiagramNode(modelParam.getModel(), inheritMap);
        result.setInheritMap(inheritMap);
        return result;
    }


    @Override
    public synchronized DataModelRltInfo bindCiRlt(BindCiRltRequestDto reqBean, Boolean fastShape, LibType libType) {
        List<String> ciCodes = Lists.newArrayList(reqBean.getSourceCiCode(), reqBean.getTargetCiCode());
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByCodes(ciCodes, reqBean.getOwnerCode(), LibType.PRIVATE);
        Map<String, ESCIInfo> ciMap = ciList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        if(BinaryUtils.isEmpty(ciMap.get(reqBean.getSourceCiCode()))){
            throw new ServerException("对象数据已被删除，无法创建关系数据。请检查!");
        }
        if(BinaryUtils.isEmpty(ciMap.get(reqBean.getTargetCiCode()))){
            throw new ServerException("对象数据已被删除，无法创建关系数据。请检查!");
        }
        reqBean.setSourceCiId(ciMap.get(reqBean.getSourceCiCode()).getId());
        reqBean.setTargetCiId(ciMap.get(reqBean.getTargetCiCode()).getId());
        Long rltId = rltSwitchSvc.bindCiRlt(reqBean, libType);
        if(BinaryUtils.isEmpty(rltId)){
            return null;
        }
        List<CcCiRltInfo> rltInfo = rltSwitchSvc.searchRltByIds(Collections.singleton(rltId), libType);
        DataModelRltInfo result = EamUtil.copy(rltInfo.get(0), DataModelRltInfo.class);
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        result.setAttributeDefs(attrClass.getAttrDefs());
        result.setAttributeCiClass(attrClass.getCiClass());
        CcCiClassInfo rltClass = rltClassApiSvc.getRltClassById(reqBean.getRltClassId());
        if(!rltClass.getCiClass().getClassName().equals(Env.ONE_TO_ONE) &&
                !rltClass.getCiClass().getClassName().equals(Env.PATERNITY) &&
                !rltClass.getCiClass().getClassName().equals(Env.ONE_TO_MANY)){
            return result;
        }
        DataModelDiagramParam modelParam = getDiagramParam(reqBean.getDiagramId(), reqBean.getSheetId());
        ESDiagramNode sourceNode = modelParam.getNodeMap().get(result.getSourceCiInfo().getCi().getCiCode());
        ESDiagramNode targetNode = modelParam.getNodeMap().get(result.getTargetCiInfo().getCi().getCiCode());
        if(fastShape){
            //快捷形状-目标端还未落到画布，需要单独处理
            targetNode = new ESDiagramNode();
            targetNode.setCiCode(reqBean.getTargetCiCode());
            targetNode.setKey(reqBean.getTargetKey());
            DiagramNodeJson nodeJson = new DiagramNodeJson();
            nodeJson.setItems(new ArrayList<>());
            targetNode.setNodeJson(JSON.toJSONString(nodeJson));
            modelParam.getNodeMap().put(reqBean.getTargetCiCode(), targetNode);
            modelParam.getKeyMap().put(reqBean.getTargetKey(), targetNode);
        }else if(BinaryUtils.isEmpty(sourceNode) || BinaryUtils.isEmpty(targetNode)){
            return result;
        }
        DiagramNodeJson sourceEntityJson = JSON.parseObject(sourceNode.getNodeJson(), DiagramNodeJson.class);
        if(BinaryUtils.isEmpty(sourceEntityJson.getItems())){
            return result;
        }
        List<String> primaryCodeList = new ArrayList<>();
        for (DataModelEntityNodeVo item : sourceEntityJson.getItems()) {
            String primaryKey = item.getAttrs().get(DataModelAttribute.PRIMARY_KEY);
            if(BinaryUtils.isEmpty(primaryKey) || !Boolean.parseBoolean(primaryKey) || primaryCodeList.contains(item.getCiCode())){
                continue;
            }
            primaryCodeList.add(item.getCiCode());
        }
        if(BinaryUtils.isEmpty(primaryCodeList)){
            return result;
        }
        CCcCi ciCdt = new CCcCi();
        ciCdt.setOwnerCodeEqual(reqBean.getOwnerCode());
        ciCdt.setCiCodes(primaryCodeList.toArray(new String[]{}));
        List<CcCiInfo> primaryCiList = iciSwitchSvc.queryCiInfoList(SysUtil.getCurrentUserInfo().getDomainId(), ciCdt, null, false, true, LibType.PRIVATE);
        List<DataModelCiInfo> inheritCiList = EamUtil.copy(primaryCiList, DataModelCiInfo.class);
        //将当前线放入LinkDataArray
        ESDiagramLink link = new ESDiagramLink();
        JSONObject linkJson = new JSONObject();
        linkJson.put("from", reqBean.getSourceKey());
        linkJson.put("to", reqBean.getTargetKey());
        linkJson.put("className", rltClass.getCiClass().getClassName());
        link.setLinkJson(JSON.toJSONString(linkJson));
        ESDiagramModel model = modelParam.getModel();
        if(BinaryUtils.isEmpty(model.getLinkDataArray())){
            model.setLinkDataArray(Lists.newArrayList());
        }
        model.getLinkDataArray().add(link);
        modelParam.setModel(model);
        Map<String, List<CcCiInfo>> inheritMap = inheritAttribute(reqBean.getSourceCiCode(), modelParam, inheritCiList);
        if(!fastShape){
            updateInheritDiagramNode(model, inheritMap);
        }
        result.setInheritMap(inheritMap);
        return result;
    }

    @Override
    public synchronized DataModelRltInfo delRltByCode(DealAttributeDto dto) {
        String rltCode = dto.getCiCode().startsWith("UK_")? dto.getCiCode().substring(3) : dto.getCiCode();
        List<CcCiRltInfo> rltList = rltSwitchSvc.getRltByCode(rltCode, dto.getOwnerCode(), LibType.PRIVATE);
        DataModelRltInfo result = new DataModelRltInfo();
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        result.setAttributeCiClass(attrClass.getCiClass());
        result.setAttributeDefs(attrClass.getAttrDefs());
        if(BinaryUtils.isEmpty(rltList)){
            return result;
        }
        rltSwitchSvc.delRltByIdsOrRltCodes(null, Sets.newHashSet(rltCode), dto.getOwnerCode(), LibType.PRIVATE);
        CcCiRltInfo rltInfo = rltList.get(0);
        CcCiClassInfo rltClass = rltClassApiSvc.getRltClassById(rltInfo.getCiRlt().getClassId());
        if(!rltClass.getCiClass().getClassName().equals(Env.ONE_TO_ONE) && !rltClass.getCiClass().getClassName().equals(Env.PATERNITY) && !rltClass.getCiClass().getClassName().equals(Env.ONE_TO_MANY)){
            return result;
        }
        DataModelDiagramParam modelParam = getDiagramParam(dto.getDiagramId(), dto.getSheetId());
        ESDiagramNode sourceNode = modelParam.getNodeMap().get(rltInfo.getSourceCiInfo().getCi().getCiCode());
        ESDiagramNode targetNode = modelParam.getNodeMap().get(rltInfo.getTargetCiInfo().getCi().getCiCode());
        if(BinaryUtils.isEmpty(sourceNode) || BinaryUtils.isEmpty(targetNode)){
            return result;
        }
        DiagramNodeJson sourceEntityJson = JSON.parseObject(sourceNode.getNodeJson(), DiagramNodeJson.class);
        if(BinaryUtils.isEmpty(sourceEntityJson.getItems())){
            return result;
        }
        List<String> primaryCodeList = new ArrayList<>();
        List<String> inheritCodeList = new ArrayList<>();
        for (DataModelEntityNodeVo item : sourceEntityJson.getItems()) {
            String primaryKey = item.getAttrs().get(DataModelAttribute.PRIMARY_KEY);
            if(BinaryUtils.isEmpty(primaryKey) || !Boolean.parseBoolean(primaryKey) || primaryCodeList.contains(item.getCiCode())){
                continue;
            }
            primaryCodeList.add(item.getCiCode());
            if(!BinaryUtils.isEmpty(item.getInheritId())){
                inheritCodeList.add(item.getInheritId());
            }
        }
        if(BinaryUtils.isEmpty(primaryCodeList)){
            return result;
        }
        if(dto.getDelFlag()){
            //删除属性,则做属性级联删除及关系删除
            Map<String, DataModelDiagramNode> inheritNodeMap = new HashMap<>(16);
            Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
            checkEntityRlt(modelParam, inheritNodeMap, new HashSet<>(), targetNode.getCiCode());
            //加入目标节点
            Map<String, List<ESDiagramLink>> currLinkGroup = new HashMap<>(1);
            List<ESDiagramLink> currLink = modelParam.getFromLinkGroup().get(sourceNode.getKey()).stream().filter(each -> dto.getCiCode().equals(each.getUniqueCode())).collect(Collectors.toList());
            currLinkGroup.put(sourceNode.getKey(), currLink);
            DataModelDiagramParam currParam = EamUtil.copy(modelParam, DataModelDiagramParam.class);
            currParam.setFromLinkGroup(currLinkGroup);
            checkEntityRlt(currParam, inheritNodeMap, new HashSet<>(), sourceNode.getCiCode());
            if(BinaryUtils.isEmpty(inheritNodeMap)) {
                return result;
            }
            Map<String, List<String>> delMap = new HashMap<>(16);
            Set<String> delCodes = new HashSet<>();
            for (DataModelDiagramNode node : inheritNodeMap.values()) {
                DiagramNodeJson json = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
                for (DataModelEntityNodeVo item : json.getItems()) {
                    if(!inheritCodeList.contains(item.getInheritId())){
                        continue;
                    }
                    delMap.putIfAbsent(node.getCiCode(), new ArrayList<>());
                    delMap.get(node.getCiCode()).add(item.getCiCode());
                    delCodes.add(item.getCiCode());
                }
            }
            result.setDelMap(delMap);
            //删除关系数据
            if(!BinaryUtils.isEmpty(delCodes)){
                List<ESCIInfo> delCiList = iciSwitchSvc.getCiByCodes(Lists.newArrayList(delCodes), dto.getOwnerCode(), LibType.PRIVATE);
                List<Long> delIds = delCiList.stream().map(CcCi::getId).collect(Collectors.toList());
                iciSwitchSvc.removeByIds(delIds, null, LibType.PRIVATE);
                updateDelDiagramNode(modelParam.getModel(), delMap);
            }
        } else {
            //不删除属性,则只将目标端实体继承属性外键改为false
            DiagramNodeJson targetJson = JSON.parseObject(targetNode.getNodeJson(), DiagramNodeJson.class);
            if(BinaryUtils.isEmpty(targetJson.getItems())){
                return result;
            }
            List<String> ciCodes = targetJson.getItems().stream().filter(each -> inheritCodeList.contains(each.getInheritId())).map(DataModelEntityNodeVo::getCiCode).distinct().collect(Collectors.toList());
            if(BinaryUtils.isEmpty(ciCodes)){
                return result;
            }
            List<ESCIInfo> attrCiList = iciSwitchSvc.getCiByCodes(ciCodes, dto.getOwnerCode(), LibType.PRIVATE);
            for (ESCIInfo each : attrCiList) {
                each.getAttrs().put(DataModelAttribute.FOREIGN_KEY, Boolean.toString(false));
            }
            Map<String, SavePrivateBatchCIContext> saveMap = ciPrivateSvc.saveOrUpdateBatchCI(attrCiList, Lists.newArrayList(attrCiList.get(0).getClassId()), dto.getOwnerCode(), dto.getOwnerCode());
            Map<String, List<CcCiInfo>> inheritMap = new HashMap<>(16);
            List<CcCiInfo> resultAttrs = new ArrayList<>();
            saveMap.forEach((ciCode, context) -> resultAttrs.add(EamUtil.coverESCIInfo(context.getEsCi(), null, null)));
            inheritMap.put(targetJson.getCiCode(), resultAttrs);
            result.setInheritMap(inheritMap);
            updateInheritDiagramNode(modelParam.getModel(), inheritMap);
        }
        return result;
    }

    @Override
    public synchronized DataModelRltInfo delEntityOrAttribute(DealAttributeDto dto) {
        List<ESCIInfo> entityList = iciSwitchSvc.getCiByCodes(dto.getCiCodeList(), dto.getOwnerCode(), LibType.PRIVATE);
        if(BinaryUtils.isEmpty(entityList)){
            throw new ServerException("未查询到当前实体信息!");
        }
        DataModelDiagramParam modelParam = getDiagramParam(dto.getDiagramId(), dto.getSheetId());
        return delEntityOrAttribute(dto, entityList, modelParam);
    }

    private DataModelRltInfo delEntityOrAttribute(DealAttributeDto dto, List<ESCIInfo> entity, DataModelDiagramParam modelParam) {
        DataModelRltInfo result = new DataModelRltInfo();
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        result.setAttributeCiClass(attrClass.getCiClass());
        result.setAttributeDefs(attrClass.getAttrDefs());

        if(BinaryUtils.isEmpty(dto.getAttrCode())){
            List<String> attrCodeList = new ArrayList<>();
            List<Long> delIds = new ArrayList<>();
            for (String entityCode : dto.getCiCodeList()) {
                List<String> inheritIds = new ArrayList<>();
                ESDiagramNode entityNode = modelParam.getNodeMap().get(entityCode);
                if(BinaryUtils.isEmpty(entityNode) || BinaryUtils.isEmpty(entityNode.getNodeJson())){
                    continue;
                }
                DiagramNodeJson entityJson = JSON.parseObject(entityNode.getNodeJson(), DiagramNodeJson.class);
                if(BinaryUtils.isEmpty(entityJson.getItems())){
                    continue;
                }
                delIds.addAll(entity.stream().map(CcCi::getId).collect(Collectors.toList()));
                for (DataModelEntityNodeVo item : entityJson.getItems()) {
                    if(BinaryUtils.isEmpty(item.getCiCode())){
                        continue;
                    }
                    attrCodeList.add(item.getCiCode());
                    boolean primaryKey = Boolean.parseBoolean(item.getAttrs().get(DataModelAttribute.PRIMARY_KEY));
                    if(!BinaryUtils.isEmpty(primaryKey) && primaryKey){
                        inheritIds.add(item.getInheritId());
                    }
                }
                if(BinaryUtils.isEmpty(inheritIds)){
                   continue;
                }
                if(dto.getDelFlag()){
                    Map<String, List<String>> delMap = delPrimaryKey(entityCode, inheritIds, modelParam);
                    for (Map.Entry<String, List<String>> each : delMap.entrySet()) {
                        result.getDelMap().putIfAbsent(each.getKey(), new ArrayList<>());
                        result.getDelMap().get(each.getKey()).addAll(each.getValue());
                    }
                }else{
                    //不删除，只将目标端实体继承节点外键置为false
                    Map<String, List<CcCiInfo>> updateAttrMap = updateForeignKey(modelParam, inheritIds, entityCode);
                    for (Map.Entry<String, List<CcCiInfo>> each : updateAttrMap.entrySet()) {
                        result.getInheritMap().putIfAbsent(each.getKey(), new ArrayList<>());
                        result.getInheritMap().get(each.getKey()).addAll(each.getValue());
                    }
                }
            }
            if(!BinaryUtils.isEmpty(attrCodeList)){
                List<ESCIInfo> attrList = iciSwitchSvc.getCiByCodes(attrCodeList, dto.getOwnerCode(), LibType.PRIVATE);
                List<Long> attrIds = attrList.stream().map(CcCi::getId).collect(Collectors.toList());
                delIds.addAll(attrIds);
            }
            if(!BinaryUtils.isEmpty(delIds)){
                iciSwitchSvc.removeByIds(delIds, null, LibType.PRIVATE);
            }
        }else{
            //删除实体属性
            List<ESCIInfo> attrList = iciSwitchSvc.getCiByCodes(dto.getAttrCode(), dto.getOwnerCode(), LibType.PRIVATE);
            if(BinaryUtils.isEmpty(attrList)){
                return result;
            }
            //校验是否能删除
            ESDiagramNode entityNode = modelParam.getNodeMap().get(dto.getCiCode());
            List<ESDiagramLink> toLinks = modelParam.getToLinkGroup().get(entityNode.getKey());
            List<ESDiagramLink> fromLinks = modelParam.getFromLinkGroup().get(entityNode.getKey());
            List<String> inheritIds = new ArrayList<>();

            for (ESCIInfo attr : attrList) {
                Object primaryObject = attr.getAttrs().get(DataModelAttribute.PRIMARY_KEY);
                boolean primaryKey = !BinaryUtils.isEmpty(primaryObject) && Boolean.parseBoolean(primaryObject.toString());
                if(!primaryKey){
                    continue;
                }
                Object foreignObject = attr.getAttrs().get(DataModelAttribute.FOREIGN_KEY);
                boolean foreignKey = !BinaryUtils.isEmpty(foreignObject) && Boolean.parseBoolean(foreignObject.toString());
                if(!BinaryUtils.isEmpty(toLinks) && foreignKey){
                    throw new ServerException("所选字段包含外键依赖无法删除,请在父表的主键中删除!");
                }
                Object inheritId = attr.getAttrs().get(DataModelAttribute.INHERIT_ID);
                if(BinaryUtils.isEmpty(inheritId)){
                    continue;
                }
                inheritIds.add(inheritId.toString());
            }
            if(!BinaryUtils.isEmpty(fromLinks)){
                if(dto.getDelFlag()){
                    Map<String, List<String>> delMap = delPrimaryKey(dto.getCiCode(), inheritIds, modelParam);
                    result.setDelMap(delMap);
                }else{
                    Map<String, List<CcCiInfo>> updateAttrMap = updateForeignKey(modelParam, inheritIds, dto.getCiCode());
                    result.setInheritMap(updateAttrMap);
                }
            }
            List<Long> delIds = attrList.stream().map(CcCi::getId).collect(Collectors.toList());
            iciSwitchSvc.removeByIds(delIds, null, LibType.PRIVATE);
        }
        return result;
    }

    @Override
    public synchronized DataModelRltInfo moveAttribute(MoveAttributeDto dto) {
        List<String> ciCodes = Lists.newArrayList(dto.getAttrCode(), dto.getSourceCiCode(), dto.getTargetCiCode());
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByCodes(ciCodes, dto.getOwnerCode(), LibType.PRIVATE);
        Map<String, ESCIInfo> ciMap = ciList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
        DataModelRltInfo result = new DataModelRltInfo();
        ESCIInfo attr = ciMap.get(dto.getAttrCode());
        if(BinaryUtils.isEmpty(attr)){
            throw new ServerException("未查询到当前实体属性信息!");
        }
        ESCIInfo sourceEntity = ciMap.get(dto.getSourceCiCode());
        if(BinaryUtils.isEmpty(sourceEntity)){
            throw new ServerException("未查询到源端实体信息!");
        }
        ESCIInfo targetEntity = ciMap.get(dto.getTargetCiCode());
        if(BinaryUtils.isEmpty(targetEntity)){
            throw new ServerException("未查询到目标端实体信息!");
        }
        Object primaryObject = attr.getAttrs().get(DataModelAttribute.PRIMARY_KEY);
        boolean primaryKey = !BinaryUtils.isEmpty(primaryObject) && Boolean.parseBoolean(primaryObject.toString());
        //源端=目标端，同实体间上下移动
        DataModelDiagramParam modelParam = getDiagramParam(dto.getDiagramId(), dto.getSheetId());
        if(!dto.getSourceCiCode().equals(dto.getTargetCiCode())){
            ESDiagramNode targetNode = modelParam.getNodeMap().get(targetEntity.getCiCode());
            DataModelAttributeParam attributeParam = getJsonName(targetNode);
            String name = attr.getAttrs().get(DataModelAttribute.NAME_CN).toString();
            DataModelEntityNodeVo attrNode = attributeParam.getNameMap().get(name);
            if(!BinaryUtils.isEmpty(attrNode)){
                throw new ServerException("当前实体已存在【"+name+"】属性项!");
            }
            Object foreignObject = attr.getAttrs().get(DataModelAttribute.FOREIGN_KEY);
            boolean foreignKey = !BinaryUtils.isEmpty(foreignObject) && Boolean.parseBoolean(foreignObject.toString());
            if(foreignKey){
                throw new ServerException("所选字段有外键依赖无法移动!");
            }
            //源端实体做删除,目标端实体做新增
            DealAttributeDto delDto = EamUtil.copy(dto, DealAttributeDto.class);
            delDto.setDelFlag(true);
            delDto.setCiCode(dto.getSourceCiCode());
            DataModelRltInfo delResult = delEntityOrAttribute(delDto, Lists.newArrayList(sourceEntity), modelParam);
            result.setDelMap(delResult.getDelMap());
            attr.setId(null);
            attr.setCiCode(null);
            //更新当前参数中该node节点数据
            ESDiagramNode sourceNode = modelParam.getNodeMap().get(dto.getSourceCiCode());
            DiagramNodeJson nodeJson = JSON.parseObject(sourceNode.getNodeJson(), DiagramNodeJson.class);
            nodeJson.getItems().removeIf(each -> each.getCiCode().equals(dto.getAttrCode()));
            sourceNode.setNodeJson(JSON.toJSONString(nodeJson));
            modelParam.getNodeMap().put(dto.getSourceCiCode(), sourceNode);
            modelParam.getKeyMap().put(sourceNode.getKey(), sourceNode);
        }else if(primaryKey && !dto.isPrimaryKey()){
            //相同实体间,键区移动到非键区,删除继承
            ESDiagramNode entityNode = modelParam.getNodeMap().get(sourceEntity.getCiCode());
            List<ESDiagramLink> fromLinks = modelParam.getFromLinkGroup().get(entityNode.getKey());
            if(!BinaryUtils.isEmpty(fromLinks)){
                String inheritId = attr.getAttrs().get(DataModelAttribute.INHERIT_ID).toString();
                Map<String, List<String>> delMap = delPrimaryKey(sourceEntity.getCiCode(), Lists.newArrayList(inheritId), modelParam);
                result.setDelMap(delMap);
            }
        }
        Long rltClassId = getDmRltClassId();
        attr.getAttrs().put(DataModelAttribute.PRIMARY_KEY, dto.isPrimaryKey());
        attr.setDiagramId(dto.getDiagramId());
        attr.setSheetId(dto.getSheetId());
        CcCiInfo ciInfo = EamUtil.coverESCIInfo(attr, null, null);
        DataModelCiInfo saveResult = saveAttribute(modelParam, targetEntity, false, rltClassId, Env.ATTRIBUTES.getCode(), ciInfo);
        result.setAttributeCiClass(saveResult.getAttributeCiClass());
        result.setAttributeDefs(saveResult.getAttributeDefs());
        result.setInheritMap(saveResult.getInheritMap());
        CcCiInfo currCi = new CcCiInfo();
        currCi.setCi(saveResult.getCi());
        currCi.setAttrs(saveResult.getAttrs());
        result.getInheritMap().put(dto.getTargetCiCode(), Lists.newArrayList(currCi));
        return result;
    }

    @Override
    public synchronized List<DataModelCopyInfo> copyEntity(DealAttributeDto dto) {
        String ownerCode = dto.getOwnerCode();
        List<ESCIInfo> ciList = iciSwitchSvc.getCiByCodes(dto.getCiCodeList(), ownerCode, LibType.PRIVATE);
        if(BinaryUtils.isEmpty(ciList)){
            throw new ServerException("未查询到当前复制实体信息!");
        }
        Map<String, SavePrivateBatchCIContext> copyEntityMap = ciPrivateSvc.copyCiListByIds(ciList, ownerCode);
        Map<String, SavePrivateBatchCIContext> originMap = copyEntityMap.values().stream().collect(Collectors.toMap(SaveBatchCIContext::getOriginCiCode, each -> each, (k1, k2) -> k2));
        List<Long> classIds = copyEntityMap.values().stream().map(SaveBatchCIContext::getClassId).distinct().collect(Collectors.toList());
        List<ESCIClassInfo> classList = ciClassApiSvc.selectCiClassByIds(classIds);
        Map<Long, ESCIClassInfo> classMap = classList.stream().collect(Collectors.toMap(ESCIClassInfo::getId, each -> each));
        CcCiClassInfo attrClass = ciClassApiSvc.getCIClassByCodes(Env.ATTRIBUTES.getCode());
        List<Long> attrClassId = Collections.singletonList(attrClass.getCiClass().getId());

        DataModelDiagramParam modelParam = getDiagramParam(dto.getDiagramId(), dto.getSheetId());
        Map<String, ESDiagramNode> nodeMap = modelParam.getNodeMap();
        List<String> oldAttrCodeList = new ArrayList<>();
        Map<String, List<String>> oldGroup = new HashMap<>(16);
        for (ESCIInfo entity : ciList) {
            ESDiagramNode node = nodeMap.get(entity.getCiCode());
            if(BinaryUtils.isEmpty(node) || BinaryUtils.isEmpty(node.getNodeJson())){
                continue;
            }
            DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
            if(BinaryUtils.isEmpty(nodeJson.getItems())){
                continue;
            }
            List<String> attrCodes = nodeJson.getItems().stream().map(DataModelEntityNodeVo::getCiCode).distinct().collect(Collectors.toList());
            oldAttrCodeList.addAll(attrCodes);
            oldGroup.put(entity.getCiCode(), attrCodes);
        }
        List<ESCIInfo> copyAttrList = new ArrayList<>();
        Map<String, SavePrivateBatchCIContext> newAttrMap = new HashMap<>(16);
        //原实体属性ciCode-uuid
        Map<String, String> oldAttrCodeMap = new HashMap<>(16);
        if(!BinaryUtils.isEmpty(oldAttrCodeList)){
            List<ESCIInfo> attrCiList = iciSwitchSvc.getCiByCodes(oldAttrCodeList, ownerCode, LibType.PRIVATE);
            Map<String, ESCIInfo> attrMap = attrCiList.stream().collect(Collectors.toMap(CcCi::getCiCode, each -> each, (k1, k2) -> k2));
            for (Map.Entry<String, List<String>> each : oldGroup.entrySet()) {
                List<String> attrCodeList = each.getValue();
                if(BinaryUtils.isEmpty(originMap.get(each.getKey()))){
                    continue;
                }
                ESCIInfo newEntity = originMap.get(each.getKey()).getEsCi();
                for (String attrCode : attrCodeList) {
                    ESCIInfo oldAttr = attrMap.get(attrCode);
                    if(BinaryUtils.isEmpty(oldAttr)){
                        continue;
                    }
                    String uuid = Long.toString(ESUtil.getUUID());
                    oldAttrCodeMap.put(oldAttr.getCiCode(), uuid);
                    ESCIInfo copy = EamUtil.copy(oldAttr, ESCIInfo.class);
                    copy.setId(null);
                    copy.setCiCode(uuid);
                    copy.getAttrs().put(DataModelAttribute.ENTITY, newEntity.getCiCode());
                    copy.getAttrs().put(DataModelAttribute.INHERIT_ID, ESUtil.getUUID());
                    copyAttrList.add(copy);
                }
            }
            if(!BinaryUtils.isEmpty(copyAttrList)){
                newAttrMap = ciPrivateSvc.saveOrUpdateBatchCI(copyAttrList, attrClassId, ownerCode, ownerCode);
            }
        }
        Long rltClassId = getDmRltClassId();
        List<BindCiRltRequestDto> bindList = new ArrayList<>();
        List<DataModelCopyInfo> result = new ArrayList<>();
        for (Map.Entry<String, SavePrivateBatchCIContext> each : originMap.entrySet()) {
            List<String> attrCodeList = oldGroup.get(each.getKey());
            SavePrivateBatchCIContext entityContext = each.getValue();
            ESCIClassInfo classInfo = classMap.get(entityContext.getClassId());
            CcCiInfo ciInfo = EamUtil.coverESCIInfo(entityContext.getEsCi(), EamUtil.copy(classInfo.getAttrDefs(), CcCiAttrDef.class), classInfo);
            DataModelCopyInfo copy = new DataModelCopyInfo();
            List<CcCiInfo> attrList = new ArrayList<>();
            if(!BinaryUtils.isEmpty(attrCodeList)){
                Long newEntityId = entityContext.getEsCi().getId();
                for (String attrCode : attrCodeList) {
                    String uuid = oldAttrCodeMap.get(attrCode);
                    SavePrivateBatchCIContext attrContext = newAttrMap.get(uuid);
                    if(BinaryUtils.isEmpty(attrContext)){
                        continue;
                    }
                    ESCIInfo newAttr = attrContext.getEsCi();
                    CcCiInfo attrCiInfo = EamUtil.coverESCIInfo(newAttr, null, null);

                    attrList.add(attrCiInfo);
                    BindCiRltRequestDto bindRlt = BindCiRltRequestDto.builder().ownerCode(ownerCode).repetitionError(false).rltClassId(rltClassId)
                            .custom1("3").sourceCiId(newEntityId).targetCiId(newAttr.getId()).build();
                    bindList.add(bindRlt);
                }
            }
            if(!BinaryUtils.isEmpty(attrList)){
                copy.setAttributeCiClass(attrClass.getCiClass());
                copy.setAttributeDefs(attrClass.getAttrDefs());
            }
            copy.setAttrList(attrList);
            CiInfoExtend extendCi = EamUtil.copy(ciInfo, CiInfoExtend.class);
            extendCi.setOriginId(entityContext.getOriginCiId());
            extendCi.setOriginCiCode(entityContext.getOriginCiCode());
            copy.setCiInfo(extendCi);
            result.add(copy);
        }
        if(!BinaryUtils.isEmpty(bindList)){
            rltSwitchSvc.bindCiRltBatch(bindList, LibType.PRIVATE);
        }
        return result;
    }

    /**
     * 删除键区实体属性,级联删除继承实体属性
     */
    private Map<String, List<String>> delPrimaryKey(String entityCode, List<String> inheritIds, DataModelDiagramParam modelParam){
        Map<String, DataModelDiagramNode> inheritNodeMap = new HashMap<>(16);
        checkEntityRlt(modelParam, inheritNodeMap, new HashSet<>(), entityCode);
        Map<String, List<String>> delMap = new HashMap<>(16);
        if(BinaryUtils.isEmpty(inheritNodeMap)){
            return delMap;
        }
        Set<String> delCodes = new HashSet<>();
        for (DataModelDiagramNode node : inheritNodeMap.values()) {
            DiagramNodeJson json = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
            for (DataModelEntityNodeVo item : json.getItems()) {
                if(!inheritIds.contains(item.getInheritId())){
                    continue;
                }
                delMap.putIfAbsent(node.getCiCode(), new ArrayList<>());
                delMap.get(node.getCiCode()).add(item.getCiCode());
                delCodes.add(item.getCiCode());
            }
        }
        if(!BinaryUtils.isEmpty(delCodes)){
            List<ESCIInfo> delCiList = iciSwitchSvc.getCiByCodes(Lists.newArrayList(delCodes), modelParam.getOwnerCode(), LibType.PRIVATE);
            List<Long> attrDelIds = delCiList.stream().map(CcCi::getId).collect(Collectors.toList());
            iciSwitchSvc.removeByIds(attrDelIds, null, LibType.PRIVATE);
        }
        updateDelDiagramNode(modelParam.getModel(), delMap);
        return delMap;
    }

    /**
     * 删除实体属性，不删除继承节点实体属性，则更新继承节点外键为false
     */
    private Map<String, List<CcCiInfo>> updateForeignKey(DataModelDiagramParam modelParam, List<String> inheritIds, String entityCode){
        Map<String, List<CcCiInfo>> result = new HashMap<>(16);
        Map<String, List<String>> updateMap = new HashMap<>(16);
        ESDiagramNode entityNode = modelParam.getNodeMap().get(entityCode);
        List<ESDiagramLink> linkList = modelParam.getFromLinkGroup().get(entityNode.getKey());
        if(BinaryUtils.isEmpty(linkList)){
            return result;
        }
        for (ESDiagramLink link : linkList) {
            JSONObject linkJson = JSON.parseObject(link.getLinkJson());
            String toKey = linkJson.get("to").toString();
            ESDiagramNode toNode = modelParam.getKeyMap().get(toKey);
            if(BinaryUtils.isEmpty(toKey) || BinaryUtils.isEmpty(toNode) || BinaryUtils.isEmpty(toNode.getCiCode())){
                continue;
            }
            DiagramNodeJson toNodeJson = JSON.parseObject(toNode.getNodeJson(), DiagramNodeJson.class);
            List<String> updateAttrList = toNodeJson.getItems().stream().filter(each -> {
                boolean emptyFlag = BinaryUtils.isEmpty(each.getInheritId());
                boolean containsFlag = inheritIds.contains(each.getInheritId());
                return !emptyFlag && containsFlag;
            }).map(DataModelEntityNodeVo::getCiCode).collect(Collectors.toList());
            updateMap.put(toNode.getCiCode(), updateAttrList);
        }
        if(BinaryUtils.isEmpty(updateMap)){
            return result;
        }
        List<String> updateAttrCode = updateMap.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
        List<ESCIInfo> updateAttrList = iciSwitchSvc.getCiByCodes(updateAttrCode, modelParam.getOwnerCode(), LibType.PRIVATE);
        for (ESCIInfo each : updateAttrList) {
            each.getAttrs().put(DataModelAttribute.FOREIGN_KEY, Boolean.toString(false));
        }
        List<Long> attrClassId = Lists.newArrayList(updateAttrList.get(0).getClassId());
        Map<String, SavePrivateBatchCIContext> saveMap = ciPrivateSvc.saveOrUpdateBatchCI(updateAttrList, attrClassId, modelParam.getOwnerCode(), modelParam.getOwnerCode());
        updateMap.forEach((ciCode, list) -> {
            List<CcCiInfo> resultAttrs = new ArrayList<>();
            for (String eachCode : list) {
                SavePrivateBatchCIContext context = saveMap.get(eachCode);
                resultAttrs.add(EamUtil.coverESCIInfo(context.getEsCi(), null, null));
            }
            result.put(ciCode, resultAttrs);
        });
        updateInheritDiagramNode(modelParam.getModel(), result);
        return result;
    }

    @Override
    public DataModelDiagramParam getDiagramParam(String energyId, String sheetId){
        DataModelDiagramParam result = new DataModelDiagramParam();
        List<ESDiagramDTO> diagramList = diagramApiClient.queryFullDiagramByIds(Collections.singletonList(energyId));
        ESDiagramInfoDTO diagram = diagramList.get(0).getDiagram();
        List<ESDiagramModel> modelList = diagram.getModelList();
        ESDiagramModel model = modelList.get(0);
        if(!BinaryUtils.isEmpty(sheetId)){
            model = modelList.stream().filter(each -> sheetId.equals(each.getSheetId())).findFirst().orElse(new ESDiagramModel());
        }
        result.setDiagram(diagram);
        result.setModel(model);
        result.setOwnerCode(diagram.getOwnerCode());
        return result;
    }

    /**
     * 获取实体和实体属性的关系分类
     */
    private Long getDmRltClassId(){
        CcCiClassInfo rltClass = rltClassApiSvc.getRltClassByName(SysUtil.getCurrentUserInfo().getDomainId(), Env.DM_INCLUDE);
        if(BinaryUtils.isEmpty(rltClass)){
            throw new ServerException("关系分类" + Env.DM_INCLUDE + "不存在!");
        }
        return rltClass.getCiClass().getId();
    }

}
